using System;
using System.Collections.Generic;
using System.Text;
using CodePlex.Resourceful;
using System.Threading;

namespace CodePlex.SpaceBlock.JobModel
{
    public class JobManager
    {

        public event EventHandler<EventArgs<Guid>> JobEnqueued;

        private List<JobWrapper> _jobs = new List<JobWrapper>();

        public bool IsBusy
        {
            get
            {
                foreach (JobWrapper wrapper in _jobs)
                {
                    if (Utils.In(wrapper.Status, JobStatus.Pending, JobStatus.Running))
                    {
                        return true;
                    }
                }
                return false;
            }
        }
        public Guid Enqueue(IJob job)
        {
            JobWrapper wrapper = new JobWrapper();
            
            wrapper.JobID = Guid.NewGuid();
            wrapper.TransferJob = job;
            _jobs.Insert(0,wrapper);
            this.OnJobEnqueued(wrapper.JobID);


            this.StartThread(wrapper);
            return wrapper.JobID;
        }


        private void StartThread(JobWrapper wrapper)
        {
            wrapper.Status = JobStatus.Pending;

            ThreadStart run = delegate
            {
                wrapper.Status = JobStatus.Running;

                try
                {
                    wrapper.TransferJob.Execute();
                    wrapper.Status = JobStatus.Completed;
                }
                catch (Exception ex)
                {
                    if (!(ex is ThreadAbortException))
                    {
                        wrapper.Exception = ex;
                        wrapper.Status = JobStatus.Failed;
                    }
                    // else assume it was cancelled

                }
                finally
                {
                    wrapper.Thread = null;
                }

            };

            Thread t = new Thread(run);
            t.IsBackground = true;
            wrapper.Thread = t;
            t.Start();
        }

        protected void OnJobEnqueued(Guid jobID)
        {
            if (this.JobEnqueued != null)
            {
                this.JobEnqueued(this, new EventArgs<Guid>(jobID));
            }
        }


        public JobInfo GetJobInformation(Guid guid)
        {
            JobWrapper jw = _jobs.Find(delegate(JobWrapper item)
            {
                return item.JobID == guid;
            });

            if (jw == null)
            {
                return null;
            }

            return GetInfo(jw);

        }

        private JobInfo GetInfo(JobWrapper job)
        {
            string caption, description;

            if (job.Exception != null)
            {
                caption = "Transfer failed.";
                description = job.Exception.Message;
            }
            else if (job.Status == JobStatus.Cancelled)
            {
                caption = "Transfer cancelled.";
                description = job.TransferJob.GetDescription();
            }
            else
            {
                caption = job.TransferJob.GetCaption();
                description = job.TransferJob.GetDescription();
            }


            string from = job.TransferJob.GetFrom();
            Path toPath = job.TransferJob.GetTargetPath();
            IFileSystem toFS = job.TransferJob.GetTargetFS();
            string to = string.Format("{1}  [{0}]", toFS.Caption, toPath);
            long? size = job.TransferJob.GetSize();
            string sizeString = size == null ? null : string.Format("{0} ({1:N0} bytes)", Utils.ComputeSizeString(size.Value), size.Value);
            long? transferred = job.TransferJob.GetTransferred();
            string transferredString = transferred == null ? null : string.Format("{0} ({1:N0} bytes)", Utils.ComputeSizeString(transferred.Value), transferred.Value);
            int progress = job.TransferJob.GetProgress();
            string speed = job.TransferJob.GetSpeed();

            return new JobInfo(job.JobID, caption, description, job.Status,
                from, to, sizeString, transferredString, speed, toFS, toPath, progress);
        }
        public IEnumerable<JobInfo> GetJobInformation()
        {
            foreach (JobWrapper job in _jobs)
            {
                yield return GetInfo(job);
            }
        }

        public void Cancel(Guid jobID)
        {
            JobWrapper w = _jobs.Find(delegate(JobWrapper item)
            {
                return item.JobID == jobID;
            });

            if (w == null || w.Status != JobStatus.Running)
            {
                return;
            }
            w.Thread.Abort();

            w.Status = JobStatus.Cancelled;
            w.Exception = null;


        }


        public void Restart(Guid jobID)
        {
            JobWrapper w = _jobs.Find(delegate(JobWrapper item)
            {
                return item.JobID == jobID;
            });
            if (w == null || !JobWrapper.IsFinished(w))
            {
                return;
            }
            w.Exception = null;
            this.StartThread(w);


        }


        public void Clear(Guid jobID)
        {
            JobWrapper w = _jobs.Find(delegate(JobWrapper item)
            {
                return item.JobID == jobID;
            });
            if (w == null || !JobWrapper.IsFinished(w))
            {
                return;
            }

            _jobs.Remove(w);

        }

        public void ClearFinished()
        {
            List<Guid> finishedJobIDs = _jobs.FindAll(JobWrapper.IsFinished).ConvertAll<Guid>(delegate(JobWrapper item)
            {
                return item.JobID;
            });


            finishedJobIDs.ForEach(this.Clear);

        }







        private class JobWrapper
        {
            public Guid JobID;
            public IJob TransferJob;
            public JobStatus Status;
            public Exception Exception;
            public Thread Thread;

            public static bool IsFinished(JobWrapper item)
            {
                return Utils.In(item.Status,
                                    JobStatus.Completed,
                        JobStatus.Failed,
                        JobStatus.Cancelled);

            }
        }




       
    }
}
